<template>
  <div class="chat-room__wrapper">
    <div class="chat-room__inner">
      <div v-if="chatLoaded" ref="chatRoomMainRef" class="chat-room__main">
        <div class="chat-room__main-content">
          <el-scrollbar ref="scrollbarRef" style="width: 100%; height: 100%;">
            <div class="inner">
              <div v-for="(item, index) in chatInfoEn.msgList" :key="index">
                <!-- 系统消息 -->
                <div v-if="item.role === 'system'" class="item system">
                  <!-- 1)文本类型 -->
                  <div v-if="item.contentType === 'text'" class="text-content">
                    <div class="text">{{ item.content }}</div>
                  </div>
                </div>

                <!-- 用户 -->
                <div v-else class="item">
                  <!-- 消息类型 -->
                  <message-type
                    :field-type="item.contentType"
                    :data-source="item"
                    :sender="item.role === oprRoleName"
                    :receiver="item.role !== oprRoleName"
                    @chatCallback="chatCallback"
                  ></message-type>
                </div>

              </div>
            </div>
          </el-scrollbar>
        </div>
      </div>


      <!-- 底部区域 -->
      <div class="chat-room__footer">
        <div class="footer-box">
          <!-- 表情、文件选择等操作 -->
          <operate-box @emoji="onSelectEmoji"></operate-box>

          <!-- 聊天输入框 -->
          <div class="input-wrapper">
            <div
              maxlength="500"
              class="inputContent common_chat_emoji-wrapper-global"
              id="common_chat_input"
              contenteditable="true"
              @paste.stop="inputContent_paste"
              @drop="inputContent_drop"
              @keydown="inputContent_keydown"
              @mouseup="inputContent_mouseup"
              @mouseleave="inputContent_mouseup"></div>
          </div>

          <!-- 发送按钮 -->
          <el-button
            type="primary"
            size="small"
            class="send-btn"
            :class="chatInfoEn.state"
            @click="sendText()"
            :disabled="chatInfoEn.inputContent.length === 0">
            发送
          </el-button>
        </div>

        <!-- 离线 -->
        <div
          v-show="chatInfoEn.state === 'off' || chatInfoEn.state === 'end'"
          class="off-wrapper"
        >
          <span class="content">会话已经结束</span>
        </div>
      </div>

    </div>
  </div>
</template>

<script>
import { getQQEmojiEmoji, getImgByFaceName } from '../../utils/qq-emoji';
import MessageType from './message-type';
import OperateBox from './operate-box';

export default {
  name: 'chat-room',
  components: { MessageType, OperateBox },
  props: {
    /**
     * 聊天信息对象，包含链接状态、消息列表、文本框内容...
     *  - msgList:
     *    {
     *      role: 角色；系统、客户
     *      contentType: 聊天信息类型；text(文本)、image(图片)等
     *    }
     */
    chatInfoEn: {
      type: Object,
      required: true,
      default: () => ({
        msgList: [],  // 消息列表
        inputContent: '', // 本次输入框的消息内容
      }),
    },
    // 操作者名称；e.g. server:服务端、client:客服端
    oprRoleName: {
      required: true,
      type: String,
      default: '',
    },
  },
  data() {
    return {
      chatLoaded: false,  // chat 是否已加载完毕
      inputContent_setTimeout: null, // 输入文字时在输入结束才修改具体内容
      selectionRange: null, // 输入框选中的区域
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.chatLoaded = true;
      this.init();
    });
  },
  methods: {
    /**
     * 初始化
     */
    init() {
      // 初始化状态
      document.getElementById('common_chat_input').innerHTML = '';

      // 在线状态
      if (this.chatInfoEn.state === 'on') {
        // 1.显示在输入框的内容
        setTimeout(() => {
          // 未断开获取焦点
          document.getElementById('common_chat_input').focus();
          this.setInputContentSelectRange();
          // 设置之前保存的输入框内容
          if (this.chatInfoEn.inputContent) {
            this.setInputDiv(this.chatInfoEn.inputContent);
          }
        }, 200);
      } else {
        document.getElementById('common_chat_input').blur();
      }

      // 2.滚动到底部
      this.$nextTick(() => {
        this.$refs.scrollbarRef.wrap.scrollTop = this.$refs.scrollbarRef.wrap.scrollHeight;
        document.getElementById('common_chat_input').focus();
      });
    },

    /**
     * 选择表情回调函数
     */
    onSelectEmoji(imgStr) {
      this.setInputDiv(imgStr);
    },

    /**
     * 输入框的粘贴
     */
    inputContent_paste: function (e) {
      let isImage = false;
      if (e.clipboardData && e.clipboardData.items.length > 0) {
        // 1.上传图片
        for (let i = 0; i < e.clipboardData.items.length; i++) {
          const item = e.clipboardData.items[i];
          if (item.kind === 'file' && item.type.indexOf('image') >= 0) {
            // 粘贴板为图片类型
            const file = item.getAsFile();
            let formData = new FormData();
            formData.append('uploadFile', file);

            // 图片上传
            // this.$http.uploadFile({
            //   url: '/upload',
            //   params: formData,
            //   successCallback: (rs) => {
            //     document.getElementById('common_chat_opr_fileUpload').value = '';
            //     this.sendMsg({
            //       contentType: 'image',
            //       fileName: rs.fileName,
            //       fileUrl: rs.fileUrl,
            //       state: 'success',
            //     });
            //   },
            // });
            isImage = true;
          }
        }

        // 2.非图片，粘贴纯文本
        if (!isImage) {
          const str = e.clipboardData.getData('text/plain');
          // 转化为纯文字
          const span = document.createElement('span');
          span.innerHTML = str;
          const txt = span.innerText;
          this.setInputDiv(
            txt
              .replace(/\n/g, '')
              .replace(/\r/g, '')
              .replace(/</g, '&lt;')
              .replace(/>/g, '&gt;'),
          );
        }
      }
      e.stopPropagation();
      e.preventDefault();
    },

    /**
     * 输入框的拖拽
     */
    inputContent_drop() {
      setTimeout(() => this.setInputContentByDiv(), 100);
    },

    /**
     * 输入框的keydown
     */
    inputContent_keydown(e) {
      // keyup触发时，绑定的数据还没有被变更，需要进行延后访问
      clearTimeout(this.$data.inputContent_setTimeout);
      this.$data.inputContent_setTimeout = setTimeout(() => {
        this.setInputContentByDiv();

        // 若按了回车，直接发送
        if (e.keyCode === 13) {
          this.sendText();
        }
        this.setInputContentSelectRange();
      }, 1);
    },

    /**
     * 输入框的mouseup
     */
    inputContent_mouseup: function (e) {
      this.setInputContentSelectRange();
    },

    /**
     * 发送文本
     */
    sendText() {
      if (this.chatInfoEn.inputContent.length === 0) {
        return;
      }
      const msgContent = this.chatInfoEn.inputContent;
      document.getElementById('common_chat_input').innerHTML = '';
      this.setInputContentByDiv();
      this.sendMsg({ contentType: 'text', content: msgContent });
    },

    /**
     * 发送消息，e.g. 文本、图片、文件
     * @param {Object} msg 消息对象
     */
    sendMsg: function (msg) {
      var self = this;
      // 1.传递
      this.$emit('sendMsg', {
        msg: msg,
        successCallbcak: function () {
          document.getElementById('common_chat_input').focus();
          self.goEnd();
        },
      });
    },

    /**
     * 设置输入内容
     * 根据input输入框innerHTML转换为纯文本
     */
    setInputContentByDiv() {
      const htmlStr = document.getElementById('common_chat_input').innerHTML;

      // 1.转换表情为纯文本：<img textanme="[笑]"/> => [笑]
      const tmpInputContent = htmlStr
        .replace(/<img .+?text=\"(.+?)\".+?>/g, '[$1]')
        .replace(/<.+?>/g, '');

      // 2.设置最长长度
      if (tmpInputContent.length > 500) {
        document.getElementById('common_chat_input').innerHTML = '';
        const value = tmpInputContent
          .substr(0, 499)
          .replace(/\[(.+?)\]/g, (item, value) => {
            return getImgByFaceName(value);
          });
        this.setInputDiv(value);
      }

      // 3.修改store
      this.chatInfoEn.inputContent = tmpInputContent;
    },

    /**
     * 设置input输入框内容
     * @param {String} value 设置的值
     */
    setInputDiv(value) {
      if (this.$data.selectionRange == null) {
        document.getElementById('common_chat_input').focus();
        return;
      }

      // 1.设置selectionRange
      if (window.getSelection) {
        window.getSelection().removeAllRanges();
        window.getSelection().addRange(this.$data.selectionRange);
      } else {
        this.$data.selectionRange && this.$data.selectionRange.select();
      }

      // 2.表情转换为img
      value = getQQEmojiEmoji(value);

      // 3.聊天框中是否选中了文本，若选中文本将被替换成输入内容
      if (window.getSelection) {
        let sel, range;
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
          // 1)删除选中的文本(内容)
          range = sel.getRangeAt(0); // 获取鼠标选中的文本区域
          range.deleteContents(); // 删除选中的文本

          // 2)创建以输入内容为内容的DocumentFragment
          let element;
          if (range.createContextualFragment) {
            element = range.createContextualFragment(value);
          } else {
            // 以下代码等同createContextualFragment
            // 创建一个DocumentFragment
            element = document.createDocumentFragment();

            let divEl = document.createElement('div');
            divEl.innerHTML = value;
            // divEl下的元素，依次插入到DocumentFragment
            for (let i = 0, len = divEl.children.length; i < len; i++) {
              element.appendChild(divEl.firstChild);
            }
          }
          // 3)选中文本的位置替换为新输入的内容，并把光标定位到新内容后方
          let lastNode = element.lastChild;
          range.insertNode(element);
          range.setStartAfter(lastNode);
          sel.removeAllRanges();
          sel.addRange(range);
        }
      } else if (document.selection && document.selection.type !== 'Control') {
        // IE < 9
        document.selection.createRange().pasteHTML(getImgByFaceName(value));
      }

      // 4.修改inputContent
      this.setInputContentByDiv();
    },

    /**
     * 设置input输入框的选择焦点
     */
    setInputContentSelectRange: function () {
      if (window.getSelection && window.getSelection().rangeCount > 0) {
        var selectRange = window.getSelection().getRangeAt(0);
        if (
          selectRange.commonAncestorContainer.nodeName === '#text' &&
          selectRange.commonAncestorContainer.parentElement &&
          selectRange.commonAncestorContainer.parentElement.id === 'common_chat_input'
        ) {
          // 选中了输入框内的文本
          this.$data.selectionRange = selectRange;
        } else if (selectRange.commonAncestorContainer.id === 'common_chat_input') {
          // 选中了输入框
          this.$data.selectionRange = selectRange;
        }
      }
    },

    /**
     * 聊天记录滚动到底部
     */
    goEnd: function () {
      this.$nextTick(() => {
        setTimeout(() => {
          this.$refs.scrollbarRef.wrap.scrollTop = this.$refs.scrollbarRef.wrap.scrollHeight;
        }, 100);
      });
    },

    /**
     * 传递回调
     */
    chatCallback(emitType, data) {
      this.$emit('chatCallback', { eventType: emitType, data: data });
    },
  },
};
</script>

<style lang="scss" scoped>
.chat-room__wrapper {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
  font-size: 12px;
  color: #fff;

  .chat-room__inner {
    width: 100%;
    height: 100%;

    .chat-room__main {
      position: relative;
      height: calc(100% - 190px);
      overflow-y: auto;
      overflow-x: hidden;

      .chat-room__main-content {
        position: absolute;
        width: 100%;
        height: 100%;

        ::v-deep .el-scrollbar__wrap {
          overflow-x: hidden;
        }

        .inner {
          padding-bottom: 20px;

          .item {
            clear: both;
            overflow: hidden;
          }

          .system {
            color: #b0b0b0;
            font-size: 12px;
            text-align: center;

            .text-content {
              padding-top: 20px;
            }
          }
        }
      }
    }

    .chat-room__footer {
      position: relative;
      width: 100%;
      padding: 0 12px;

      .footer-box {
        border-top: 1px solid rgba(53, 157, 255, 0.4);

        .input-wrapper {
          position: relative;
          padding: 2px 0 0 10px;

          .inputContent {
            width: 99%;
            padding: 2px;
            height: 85px;
            resize: none;
            overflow: auto;
            line-height: 1.5;
            outline: 0 solid transparent;
          }
        }

        .send-btn {
          float: right;
          margin-right: 16px;

          &.off,
          &.end {
            background-color: #ccc;
            border-color: #ccc;
          }
        }
      }

      .off-wrapper {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(255, 255, 255, 0.6);
        font-size: 14px;

        .content {
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        }
      }
    }
  }
}
</style>
